package v1

import (
	"context"
	"slices"
	"time"

	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
	"google.golang.org/protobuf/types/known/emptypb"
	"google.golang.org/protobuf/types/known/timestamppb"

	"quipus/internal/utils"
	v1pb "quipus/proto/gen/api/v1"
	apb "quipus/proto/gen/assist"
)

func (s *APIV1Service) SetKnotResources(ctx context.Context, request *v1pb.SetKnotResourcesRequest) (*emptypb.Empty, error) {
	knotID, err := utils.ExtractIDFromUrn(utils.KnotUrnPrefix, request.Urn)
	if err != nil {
		return nil, status.Errorf(codes.InvalidArgument, "invalid knot urn: %v", err)
	}
	resources, _, err := s.Gds.Handler.ListResources(ctx, &apb.FindResource{
		KnotId: knotID,
	})
	if err != nil {
		return nil, status.Errorf(codes.Internal, "failed to list resources")
	}

	// Delete resources that are not in the request.
	for _, resource := range resources {
		found := false
		for _, requestResource := range request.Resources {
			if resource.UID == requestResource.Uid {
				found = true
				break
			}
		}
		if !found {
			if err = s.Gds.Handler.DeleteResource(ctx, &apb.DeleteResource{
				Id:     int32(resource.ID),
				KnotId: knotID,
			}); err != nil {
				return nil, status.Errorf(codes.Internal, "failed to delete resource")
			}
		}
	}

	slices.Reverse(request.Resources)
	localTs := time.Now().Unix()
	// Update resources' knot_id in the request.
	for index, resource := range request.Resources {
		id, err := utils.ExtractIDFromUrn(utils.ResourceUrnPrefix, resource.Urn)
		if err != nil {
			return nil, status.Errorf(codes.InvalidArgument, "invalid resource name: %v", err)
		}
		updatedTs := localTs + int64(index)
		if err := s.Gds.Handler.UpdateResource(ctx, &apb.UpdateResource{
			Id:        id,
			KnotId:    knotID,
			UpdatedTs: timestamppb.New(time.Unix(updatedTs, 0)),
		}); err != nil {
			return nil, status.Errorf(codes.Internal, "failed to update resource: %v", err)
		}
	}

	return &emptypb.Empty{}, nil
}

func (s *APIV1Service) ListKnotResources(ctx context.Context, request *v1pb.ListKnotResourcesRequest) (*v1pb.ListKnotResourcesResponse, error) {
	id, err := utils.ExtractIDFromUrn(utils.KnotUrnPrefix, request.Urn)
	if err != nil {
		return nil, status.Errorf(codes.InvalidArgument, "invalid knot urn: %v", err)
	}
	resources, _, err := s.Gds.Handler.ListResources(ctx, &apb.FindResource{
		KnotId: id,
	})
	if err != nil {
		return nil, status.Errorf(codes.Internal, "failed to list resources: %v", err)
	}

	response := &v1pb.ListKnotResourcesResponse{
		Resources: []*v1pb.Resource{},
	}
	for _, resource := range resources {
		response.Resources = append(response.Resources, s.convertResourceFromDB(ctx, resource))
	}
	return response, nil
}
